﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace StockFinder.Model
{
    public static class Extensions
    {
        /// <summary>
        /// Gets the average volume over the last 3 months
        /// </summary>
        /// <param name="prices"></param>
        /// <returns></returns>
        public static decimal AverageVolume(this IOrderedEnumerable<DailyPrice> prices)
        {    
            DateTime lookBackDate = DateTime.Today.AddMonths(-3);
            return AverageVolume(prices, lookBackDate);
        }

        /// <summary>
        /// Gets the average volume over the specified number of days
        /// </summary>
        /// <param name="prices"></param>
        /// <param name="daysBack"></param>
        /// <returns></returns>
        public static decimal AverageVolume(this IOrderedEnumerable<DailyPrice> prices, DateTime lookBackDate)
        {
            return prices.Where(p => p.PriceDate < lookBackDate).Average(p => Convert.ToDecimal(p.Volume));
        }

        /// <summary>
        /// Gets the all time adjusted close price
        /// </summary>
        /// <param name="prices"></param>
        /// <returns></returns>
        public static DailyPrice AllTimeHighestPriceByAdjustedClose(this IOrderedEnumerable<DailyPrice> prices)
        {
            DailyPrice high = null;

            if (prices.Count() > 0)
            {
                decimal allTimeHighPrice = prices.Max(price => price.AdjustedClose);

                high = prices.First(price => price.AdjustedClose == allTimeHighPrice);
            }

            return high;
        }

        /// <summary>
        /// Gets the all time adjusted close price
        /// </summary>
        /// <param name="prices"></param>
        /// <returns></returns>
        public static DailyPrice AllTimeHighestPriceByAdjustedClose(this IEnumerable<DailyPrice> prices)
        {
            DailyPrice high = null;

            if (prices.Count() > 0)
            {
                decimal allTimeHighPrice = prices.Max(price => price.AdjustedClose);

                high = prices.First(price => price.AdjustedClose == allTimeHighPrice);
            }

            return high;
        }

        /// <summary>
        /// Gets the all time adjusted high price
        /// </summary>
        /// <param name="prices"></param>
        /// <returns></returns>
        public static DailyPrice AllTimeHighestPriceByAdjustedHigh(this IOrderedEnumerable<DailyPrice> prices)
        {
            DailyPrice high = null;

            if (prices.Count() > 0)
            {
                decimal allTimeHighPrice = prices.Max(price => price.AdjustedHigh);

                high = prices.First(price => price.AdjustedHigh == allTimeHighPrice);
            }

            return high;
        }

        /// <summary>
        /// Gets the all time adjusted high price
        /// </summary>
        /// <param name="prices"></param>
        /// <returns></returns>
        public static DailyPrice AllTimeHighestPriceByAdjustedHigh(this IEnumerable<DailyPrice> prices)
        {
            DailyPrice high = null;

            if (prices.Count() > 0)
            {
                decimal allTimeHighPrice = prices.Max(price => price.AdjustedHigh);

                high = prices.First(price => price.AdjustedHigh == allTimeHighPrice);
            }

            return high;
        }

        /// <summary>
        /// Gets the all time adjusted low price
        /// </summary>
        /// <param name="prices"></param>
        /// <returns></returns>
        public static DailyPrice AllTimeLowestPriceByAdjustedLow(this IOrderedEnumerable<DailyPrice> prices)
        {
            DailyPrice low = null;

            if (prices.Count() > 0)
            {
                //ignore zero price
                IEnumerable<DailyPrice> pricesAboveZero = prices.Where(price => price.AdjustedLow > 0);

                //make sure we have some prices
                if (pricesAboveZero.Count() > 0)
                {
                    decimal allTimeLowPrice = prices.Min(price => price.AdjustedLow);

                    low = prices.First(price => price.AdjustedHigh == allTimeLowPrice);
                }                
            }

            return low;
        }      

        public static IEnumerable<WeeklyPrice> AsWeeklyPrices(this IOrderedEnumerable<DailyPrice> dailyPrices)
        {
            List<WeeklyPrice> weeklyPrices = null;

            //make sure we have some prices
            if (dailyPrices != null && dailyPrices.Count() > 0)
            {
                weeklyPrices = new List<WeeklyPrice>();

                //loop the daily prices
                foreach (DailyPrice dailyPrice in dailyPrices)
                {
                    //duff prices?
                    if (dailyPrice.AdjustedOpen == 0)
                    {
                        //yes so quit
                        break;
                    }

                    //get the week of year
                    int weekOfYear = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
                        dailyPrice.PriceDate,
                        CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
                        CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek);

                    //does this week/year combination already exist?
                    WeeklyPrice weeklyPrice = weeklyPrices.GetExistingWeeklyPrice(weekOfYear, dailyPrice.PriceDate.Year);

                    //does it exist?
                    if (weeklyPrice == null)
                    {
                        //no so create and setup
                        weeklyPrice = new WeeklyPrice();
                        weeklyPrice.Year = dailyPrice.PriceDate.Year;
                        weeklyPrice.WeekOfYear = weekOfYear;

                        //add to list
                        weeklyPrices.Add(weeklyPrice);
                    }

                    //set day of week
                    switch (dailyPrice.PriceDate.DayOfWeek)
                    {
                        case DayOfWeek.Monday:

                            weeklyPrice.Monday = dailyPrice;

                            break;

                        case DayOfWeek.Tuesday:

                            weeklyPrice.Tuesday = dailyPrice;

                            break;

                        case DayOfWeek.Wednesday:

                            weeklyPrice.Wednesday = dailyPrice;

                            break;

                        case DayOfWeek.Thursday:

                            weeklyPrice.Thursday = dailyPrice;

                            break;

                        case DayOfWeek.Friday:

                            weeklyPrice.Friday = dailyPrice;

                            break;

                        default:
                            //do nothing
                            break;
                    }
                }
            }

            return weeklyPrices;
        }

        public static WeeklyPrice GetExistingWeeklyPrice(this List<WeeklyPrice> weeklyPrices, int weekOfYear, int year)
        {
            WeeklyPrice matchingWeeklyPrice = null;

            foreach (WeeklyPrice weeklyPrice in weeklyPrices)
            {
                if (weeklyPrice.WeekOfYear == weekOfYear &&
                    weeklyPrice.Year == year)
                {
                    matchingWeeklyPrice = weeklyPrice;
                }
            }

            return matchingWeeklyPrice;
        }

        /// <summary>
        /// Gets the daily SMA for the number of days back
        /// </summary>
        /// <param name="dailyPrices"></param>
        /// <param name="numberOfDays"></param>
        /// <returns></returns>
        public static decimal GetSimpleMovingAverage(this IOrderedEnumerable<DailyPrice> dailyPrices, int numberOfDays)
        {
            decimal result = 0;

            //make sure have enough days
            if (dailyPrices.Count() >= numberOfDays)
            {
                result = dailyPrices.Take(numberOfDays).Average(i => i.AdjustedClose);
            }

            return result;
        }

        /// <summary>
        /// Gets the weekly SMA for number of weeks back
        /// </summary>
        /// <param name="weeklyPrices"></param>
        /// <param name="numberOfWeeks"></param>
        /// <returns></returns>
        public static decimal GetSimpleMovingAverage(this IEnumerable<WeeklyPrice> weeklyPrices, int numberOfWeeks)
        {
            decimal result = 0;

            //make sure have enough days
            if (weeklyPrices.Count() >= numberOfWeeks)
            {
                result = weeklyPrices.Take(numberOfWeeks).Average(i => i.GetWeeklyClose());
            }

            return result;
        }

        public static void GenerateAverageTrueRange(this IEnumerable<DailyPrice> prices)
        {

        }
    }
}
